home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 2
/
Atari Mega Archive CD - Volume 2.iso
/
minix
/
up1510b.tgz
/
up1510b
/
src
/
commands
/
mail.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-07-23
|
16KB
|
700 lines
/* mail - send/receive mail Author: Peter S. Housel */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#undef EOF /* temporary hack */
#include <signal.h>
#include <pwd.h>
#include <time.h>
#include <setjmp.h>
#include <string.h>
#include <stdio.h>
#ifdef DEBUG
#define D(Q) (Q)
#else
#define D(Q)
#endif
#define SHELL "/bin/sh"
#define DROPNAME "/usr/spool/mail/%s"
#define LOCKNAME "/usr/spool/mail/%s.lock"
#define LOCKWAIT 5 /* seconds to wait after collision */
#define LOCKTRIES 4 /* maximum number of collisions */
#define MBOX "mbox"
#define HELPFILE "/usr/lib/mail.help"
#define PROMPT "? "
#define PATHLEN 80
#define MAXRCPT 100 /* maximum number of recipients */
#define LINELEN 512
/* #define MAILER "/usr/bin/smail" /* smart mailer */
#define MAILERARGS /* (unused) */
#define UNREAD 1 /* 'not read yet' status */
#define DELETED 2 /* 'deleted' status */
#define READ 3 /* 'has been read' status */
extern int optind; /* getopt() globals */
extern char *optarg;
extern int errno;
extern char *malloc();
struct letter {
struct letter *prev, *next; /* linked letter list */
int status; /* letter status */
off_t location; /* location within mailbox file */
};
struct letter *firstlet, *lastlet;
int usemailer = 1; /* use MAILER to deliver (if any) */
int printmode = 0; /* print-and-exit mode */
int quitmode = 0; /* take interrupts */
int reversemode = 0; /* print mailbox in reverse order */
int usedrop = 1; /* read the maildrop (no -f given) */
int verbose = 0; /* pass "-v" flag on to mailer */
int needupdate = 0; /* need to update mailbox */
char mailbox[PATHLEN]; /* user's mailbox/maildrop */
char tempname[PATHLEN] = "/tmp/mailXXXXXX"; /* temporary file */
FILE *boxfp = NULL; /* mailbox file */
jmp_buf printjump; /* for quitting out of letters */
unsigned oldmask; /* saved umask() */
int deliver();
FILE *makerewindable();
int copy();
void readbox(), printall(), interact(), savelet(), updatebox();
void printlet(), doshell(), usage();
void onint();
char *basename(), *whoami();
extern FILE *fopen(), *freopen(), *fdopen();
extern struct passwd *getpwnam(), *getpwuid();
extern char *getenv();
main(argc, argv)
int argc;
char *argv[];
{
int c;
if ('l' == (basename(argv[0]))[0]) /* 'lmail' link? */
usemailer = 0; /* yes, let's deliver it */
(void) mktemp(tempname); /* name the temp file */
oldmask = umask(022); /* change umask for security */
while (EOF != (c = getopt(argc, argv, "epqrf:t:dv"))) switch (c) {
case 'e':
case 't':
fprintf(stderr, "option not implemented yet\n");
/* Because I don't know what they do */
exit(1);
case 'p': ++printmode; break;
case 'q': ++quitmode; break;
case 'r': ++reversemode; break;
case 'f':
setuid(getuid()); /* won't need to lock */
usedrop = 0;
strncpy(mailbox, optarg, PATHLEN - 1);
break;
case 'd': usemailer = 0; break;
case 'v': ++verbose; break;
default:
usage();
exit(1);
}
if (optind < argc) {
if (deliver(argc - optind, argv + optind) < 0)
exit(1);
else
exit(0);
}
if (usedrop) sprintf(mailbox, DROPNAME, whoami());
D(printf("mailbox=%s\n", mailbox));
readbox();
if (printmode)
printall();
else
interact();
if (needupdate) updatebox();
exit(0);
}
int deliver(count, vec)
int count;
char *vec[];
{
int i;
int errs = 0; /* count of errors */
int dropfd; /* file descriptor for user's drop */
int created = 0; /* true if we created the maildrop */
FILE *mailfp; /* fp for mail */
struct stat stb; /* for checking drop modes, owners */
void (*sigint) (), (*sighup) (), (*sigquit) (); /* saving signal state */
time_t now; /* for datestamping the postmark */
char sender[32]; /* sender's login name */
char lockname[PATHLEN]; /* maildrop lock */
int locktries; /* tries when box is locked */
struct passwd *pw; /* sender and recipent */
if (count > MAXRCPT) {
fprintf(stderr, "mail: too many recipients\n");
return -1;
}
#ifdef MAILER
if (usemailer) {
char *argvec[MAXRCPT + 3];
char **argp;
setuid(getuid());
argp = argvec;
*argp++ = "send-mail";
if (verbose) *argp++ = "-v";
for (i = 0; i < count; ++i) *argp++ = vec[i];
*argp = (char *) NULL;
execv(MAILER, argvec);
fprintf(stderr, "mail: couldn't exec %s\n", MAILER);
return -1;
}
#endif /* MAILER */
if (NULL == (pw = getpwuid(getuid()))) {
fprintf(stderr, "mail: unknown sender\n");
return -1;
}
strcpy(sender, pw->pw_name);
/* If we need to rewind stdin and it isn't rewindable, make a copy */
if (isatty(0) || (count > 1 && lseek(0, 0L, 0) < 0L)) {
mailfp = makerewindable();
} else
mailfp = stdin;
/* Shut off signals during the delivery */
sigint = signal(SIGINT, SIG_IGN);
sighup = signal(SIGHUP, SIG_IGN);
sigquit = signal(SIGQUIT, SIG_IGN);
for (i = 0; i < count; ++i) {
if (count > 1) rewind(mailfp);
D(printf("deliver to %s\n", vec[i]));
if (NULL == (pw = getpwnam(vec[i]))) {
fprintf(stderr, "mail: user %s not known\n", vec[i]);
++errs;
continue;
}
sprintf(mailbox, DROPNAME, pw->pw_name);
sprintf(lockname, LOCKNAME, pw->pw_name);
D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname));
/* Lock the maildrop while we're messing with it. Races are
* possible (though not very likely) when we have to create
* the maildrop, but not otherwise. If the box is already
* locked, wait awhile and try again. */
locktries = created = 0;
trylock:
if (link(mailbox, lockname) != 0) {
if (ENOENT == errno) { /* user doesn't have a drop yet */
if ((dropfd = creat(mailbox, 0600)) < 0) {
fprintf(stderr, "mail: couln't create a maildrop for user %s\n",
vec[i]);
++errs;
continue;
}
++created;
goto trylock;
} else { /* somebody else has it locked, it seems -
* wait */
if (++locktries >= LOCKTRIES) {
fprintf(stderr, "mail: couldn't lock maildrop for user %s\n",
vec[i]);
++errs;
continue;
}
sleep(LOCKWAIT);
goto trylock;
}
}
if (created) {
(void) chown(mailbox, pw->pw_uid, pw->pw_gid);
boxfp = fdopen(dropfd, "a");
} else
boxfp = fopen(mailbox, "a");
if (NULL == boxfp || stat(mailbox, &stb) < 0) {
fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]);
unlink(lockname);
++errs;
continue;
}
if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]);
unlink(lockname);
++errs;
continue;
}
(void) time(&now);
fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now));
if ((copy(mailfp, boxfp) < 0) | (fclose(boxfp) != 0)) {
fprintf(stderr, "mail: error delivering to user %s", vec[i]);
perror("");
++errs;
}
unlink(lockname);
}
fclose(mailfp);
/* Put signals back the way they were */
signal(SIGINT, sigint);
signal(SIGHUP, sighup);
signal(SIGQUIT, sigquit);
return(0 == errs) ? 0 : -1;
}
/* 'stdin' isn't rewindable. Make a temp file that is.
* Note that if one wanted to catch SIGINT and write a '~/dead.letter'
* for interactive mails, this might be the place to do it (though the
* case where a MAILER is being used would also need to be handled).
*/
FILE *makerewindable()
{
FILE *tempfp; /* temp file used for copy */
int c; /* character being copied */
int state; /* ".\n" detection state */
if (NULL == (tempfp = fopen(tempname, "w"))) {
fprintf(stderr, "mail: can't create temporary file");
return NULL;
}
/* Here we copy until we reach the end of the letter (end of file or
* a line containing only a '.'), painstakingly avoiding setting a
* line length limit. */
state = '\n';
while (EOF != (c = getc(stdin))) switch (state) {
case '\n':
if ('.' == c)
state = '.';
else {
if ('\n' != c) state = '\0';
putc(c, tempfp);
}
break;
case '.':
if ('\n' == c) goto done;
state = '\0';
putc('.', tempfp);
putc(c, tempfp);
break;
default:
state = ('\n' == c) ? '\n' : '\0';
putc(c, tempfp);
}
done:
if (ferror(tempfp) || fclose(tempfp)) {
fprintf(stderr, "mail: couldn't copy letter to temporary file\n");
return NULL;
}
tempfp = freopen(tempn